const { WebSocketServer } = require('ws');
const { createServer } = require('http');

// 1. 初始化服务 (建议结合HTTP服务)
const server = createServer();
const wss = new WebSocketServer({ server });

// 2. 连接存储（支持快速查找和遍历）
const deviceConnections = new Map(); // deviceId -> ws

// 3. 心跳检测配置
const HEARTBEAT_INTERVAL = 30000; // 30秒
const HEARTBEAT_TIMEOUT = 5000;   // 5秒无响应超时

// 4. 连接处理
wss.on('connection', (ws, req) => {
  // 4.1 身份验证
  const deviceId = authenticateConnection(req);
  if (!deviceId) {
    ws.close(1008, 'Authentication failed');
    return;
  }

  // 4.2 处理重复连接
  handleDuplicateConnection(deviceId, ws);

  // 4.3 心跳设置
  setupHeartbeat(ws);

  // 4.4 消息处理器
  bindMessageHandler(ws);

  // 4.5 清理逻辑
  bindCleanupHandler(ws, deviceId);
});

// 5. 启动服务
server.listen(8080, () => {
  console.log(`WebSocket服务已启动 ws://0.0.0.0:8080`);
});

// --------------------------
// 工具函数（实际项目建议拆分为模块）
// --------------------------

function authenticateConnection(req) {
  const params = new URLSearchParams(req.url.split('?')[1] || '');
  const deviceId = params.get('deviceId');
  const token = params.get('token');

  // 示例验证逻辑（生产环境需加强）
  if (!deviceId || !token) return null;
  return validateToken(deviceId, token) ? deviceId : null;
}

function handleDuplicateConnection(deviceId, ws) {
  const existingConn = deviceConnections.get(deviceId);
  if (existingConn) {
    existingConn.close(1001, 'Duplicate connection'); // 1001: Going Away
  }
  deviceConnections.set(deviceId, ws);
  ws.deviceId = deviceId;
}

function setupHeartbeat(ws) {
  ws.isAlive = true;
  
  ws.on('pong', () => {
    ws.isAlive = true;
    ws.lastPongTime = Date.now();
  });

  // 初始ping（立即触发一次）
  ws.ping();
}

function bindMessageHandler(ws) {
  ws.on('message', (rawData) => {
    try {
      const message = parseMessage(rawData);
      validateMessageStructure(message);
      
      // 消息路由
      routeMessage(ws, message);
      
    } catch (err) {
      handleMessageError(ws, err);
    }
  });
}

function bindCleanupHandler(ws, deviceId) {
  ws.on('close', () => {
    if (deviceConnections.get(deviceId) === ws) {
      deviceConnections.delete(deviceId);
      console.log(`设备 ${deviceId} 连接已关闭`);
    }
  });

  ws.on('error', (err) => {
    console.error(`设备 ${deviceId} 连接错误:`, err);
  });
}

// --------------------------
// 消息处理核心
// --------------------------

function routeMessage(senderWs, message) {
  // 1. 广播消息
  if (message.isBroadcast) {
    broadcastMessage(senderWs, message);
    return;
  }

  // 2. 单播消息
  const targetWs = deviceConnections.get(message.targetDeviceId);
  if (!targetWs) {
    throw new Error(`目标设备 ${message.targetDeviceId} 离线`);
  }

  // 添加元数据后转发
  targetWs.send(JSON.stringify({
    ...message,
    metadata: {
      sender: senderWs.deviceId,
      timestamp: Date.now()
    }
  }));

  // 发送ACK
  sendAck(senderWs, message.msgId);
}

function broadcastMessage(senderWs, message) {
  deviceConnections.forEach((ws, deviceId) => {
    if (ws !== senderWs && ws.readyState === ws.OPEN) {
      ws.send(JSON.stringify(message));
    }
  });
}

// --------------------------
// 定时任务
// --------------------------

// 心跳检测
setInterval(() => {
  const now = Date.now();
  deviceConnections.forEach((ws, deviceId) => {
    if (!ws.isAlive || (ws.lastPongTime && now - ws.lastPongTime > HEARTBEAT_TIMEOUT)) {
      console.log(`设备 ${deviceId} 心跳超时，强制关闭`);
      ws.terminate();
    } else {
      ws.isAlive = false;
      ws.ping();
    }
  });
}, HEARTBEAT_INTERVAL);

// --------------------------
// 其他工具函数
// --------------------------

function parseMessage(rawData) {
  try {
    return JSON.parse(rawData);
  } catch {
    throw new Error('消息格式无效，必须是JSON');
  }
}

function validateMessageStructure(msg) {
  if (!msg.msgId) throw new Error('缺少消息ID');
  if (!msg.command && !msg.isBroadcast) {
    throw new Error('消息必须包含command或isBroadcast标记');
  }
}

function sendAck(ws, msgId) {
  ws.send(JSON.stringify({
    type: 'ack',
    msgId,
    timestamp: Date.now()
  }));
}

function handleMessageError(ws, error) {
  console.error('消息处理错误:', error.message);
  ws.send(JSON.stringify({
    status: 'error',
    error: error.message,
    timestamp: Date.now()
  }));
}